#include "global.h"

// Syscall function pointer
typedef (*syscaller)();

// Available syscalls
void getProcessID();
void printStatus();
void changeName(char* name);
void goToKernelMode();
void kernelSwitch();
void wait(int* exitValue);
void fork();
void createpipe(int* pfd);
void pfd();
void close();
void read();
void write();
void exec(char* filename);
void exit(int exitValue);
void sysgetc();
void sysputc(char c);
void changeColor();

syscaller	syscalls[17] = { getProcessID, printStatus, changeName, goToKernelMode,
				kernelSwitch, wait, exit, fork, exec, createpipe, pfd,
				close, read, write, sysgetc, sysputc, changeColor };

// The handler responsible for calling the right syscall
void syscallHandler(int a)
{
	int segment = running->userStackSegment;
	int offset = running->userStackPointer;
	u16 a = 0, b = 0, c = 0, d = 0;
	
	a = getWord(segment, offset + 26);
	b = getWord(segment, offset + 28);
	c = getWord(segment, offset + 30);
	d = getWord(segment, offset + 32);
	
	if (a < 17)
	{
		syscalls[a](b, c, d);
	}
	else
	{
		printf("Invalid system call!\n");
	}
}

void installInt80()
{
	putWord((int)int80h, 0x0, 320);
	putWord(0x1000, 0x0, 322);
}

void getProcessID()
{
	putWord(running->processID, running->userStackSegment,
		running->userStackPointer + 16);
}

void printStatus()
{
	int i = 0, j = 0;
	printf("Name           Status   PID   PPID\n");
	printf("----------------------------------\n");
	for (i = 0; i < NUM_PROCS; ++i)
	{
		printf("%s", processes[i].name);
		for (j = strlen(processes[i].name); j < 15; ++j)
			putc(' ');
		switch(processes[i].status)
		{
			case DEAD: printf("DEAD     "); break;
			case FREE: printf("FREE     "); break;
			case READY: printf("READY    "); break;
			case SLEEP: printf("SLEEP    "); break;
			case ZOMBIE: printf("ZOMBIE   "); break;
		}
		if (processes[i].status != FREE)
			printf("%d     %d\n", processes[i].processID, processes[i].parentProcessID);
		else
			printf("%d\n", processes[i].processID);
	}
}

void changeName(char* name)
{
	int i = 0;
	char curChar = '\0';
	do
	{
		curChar = get_byte(running->userStackSegment, name + i);
		running->name[i] = curChar;
		++i;
	} while (curChar != '\0');
}

void goToKernelMode()
{
	processBody();
}

void kernelSwitch()
{
	tswitch();
}

void wait(int* exitValue)
{
	int status;
	kwait(&status);
	putWord(status, running->userStackSegment, running->userStackPointer + 16);
}

void fork()
{
	Process* newProcess = dequeue(&freeQueue);
	if (newProcess)
	{
		int i, segment = 0x1000;

		// Initialize CPU registers
		for (i = 1; i < 10; ++i)
		{
			newProcess->stack[STACK_SIZE - i] = 0;
		}
		
		// Initialize the new process
		newProcess->stack[STACK_SIZE - 1] = (int)goUmode;
		newProcess->stackPointer = &(newProcess->stack[STACK_SIZE - 9]);		
		newProcess->parentProcessID = running->processID;
		newProcess->status = READY;
		newProcess->numChildren = 0;
		enqueue(newProcess, &readyQueue);
		
		// Up the parent child count
		++running->numChildren;
		
		// Copy all fds from the parent
		for (i = 0; i < NUM_FILE_DESC; ++i)
		{
			newProcess->fileDescriptors[i] = running->fileDescriptors[i];
			if (running->fileDescriptors[i] != 0)
			{
				++running->fileDescriptors[i]->refCount;
				if (running->fileDescriptors[i]->mode == READ_PIPE)
					++running->fileDescriptors[i]->pipePtr->numReader;
				else if (running->fileDescriptors[i]->mode == WRITE_PIPE)
					++running->fileDescriptors[i]->pipePtr->numWriter;
			}
		}
		
		// Load the user mode program into the processes memory space
		segment *= newProcess->processID + 1;
		
		// Initialize the user stack
		for (i = 0; i < 0x1000; i += 2)
		{
			short word = getWord(running->userStackSegment, i);
			putWord(word, segment, i);
		}
		
		// Modify the stack
		putWord(segment, segment, running->userStackPointer);	// Set uDS
		putWord(segment, segment, running->userStackPointer + 2);	// Set uES
		putWord(segment, segment, running->userStackPointer + 20);	// Set the CS register
		putWord(0x0200, segment, running->userStackPointer + 22);	// Set the flag register
		
		// Set the uss and usp of the new process
		newProcess->userStackSegment = segment;
		newProcess->userStackPointer = running->userStackPointer;
		
		// Return the child PID to the running process
		putWord(newProcess->processID, running->userStackSegment,
			running->userStackPointer + 16);
			
		// Return 0 to the child process
		putWord(0, newProcess->userStackSegment,
			newProcess->userStackPointer + 16);
	}
	else
		putWord(-1, running->userStackSegment, running->userStackPointer + 16);
}

void createpipe(int* pfd)
{
	int kpfd[2];
	do_pipe(kpfd);
	putWord(kpfd[0], running->userStackSegment, pfd);
	putWord(kpfd[1], running->userStackSegment, pfd + 1);
}

void pfd()
{
	int i = 0, j = 0;
	printf(" FD   Type   Mode\n");
	printf("------------------\n");
	for (i = 0; i < NUM_FILE_DESC; ++i)
	{
		if (running->fileDescriptors[i] != 0)
		{
			printf(" %d    ", i);
			switch(fileEntries[i].mode)
			{
				case READ: printf("FILE   READ\n"); break;
				case READ_PIPE: printf("PIPE   READ\n"); break;
				case WRITE: printf("FILE WRITE\n"); break;
				case WRITE_PIPE: printf("PIPE   WRITE\n"); break;
			}
		}
	}
}

void close(int fd)
{
	kclose(fd);
}

void read(int fd, char* buf, int nbytes)
{
	int bytesRead, i;
	char kbuf[PIPE_SIZE], *kbufptr = kbuf;
	bytesRead = read_pipe(fd, kbuf, nbytes);
	for (i = 0; i < bytesRead; ++i)
	{
		put_byte(*(kbufptr++), running->userStackSegment, buf++);
	}
	putWord(bytesRead, running->userStackSegment, running->userStackPointer + 16);
}

void write(int fd, char* buf, int nbytes)
{
	int bytesWritten, i;
	char kbuf[100];
	for (i = 0; i < nbytes; ++i)
	{
		kbuf[i] = get_byte(running->userStackSegment, buf++);
	}
	bytesWritten = write_pipe(fd, kbuf, nbytes);
	putWord(bytesWritten, running->userStackSegment, running->userStackPointer + 16);
}

void exec(char* filename)
{
	int i = 0;
	int segment = running->userStackSegment;
	char buf[30];
	do
	{
		buf[i] = get_byte(running->userStackSegment, filename + i);
	} while (buf[i++] != '\0');
	
	// Load the program into the user space
	load(buf, running->userStackSegment);
	
	// Reset the ustack for execution of the new image
	putWord(0x0200, segment, 0x1000 - 2);	// Set the flag register
	putWord(segment, segment, 0x1000 - 4);	// Set the flag register
	// Set ax, bx, cx, dx, bp, si, di to 0
	for (i = 6; i < 21; i += 2)
	{
		putWord(0, segment, 0x1000 - i);
	}
	putWord(segment, segment, 0x1000 - 22);	// Set the uES to seg
	putWord(segment, segment, 0x1000 - 24);	// Set the uDS to seg
	
	running->userStackPointer = 0x1000 - 24;
	goUmode();
}

void exit(int exitValue)
{
	kexit(exitValue);
}

void sysgetc()
{
	putWord(getc(), running->userStackSegment, running->userStackPointer + 16);
}

void sysputc(char c)
{
	putc(c);
}

/*BLACK = 0, BLUE, GREEN, CYAN, RED, PURPLE, YELLOW, WHITE,
  HBLACK, HBLUE, HGREEN, HCYAN, HRED, HPURPLE, HYELLOW, HWHITE*/
void changeColor()
{
	int choice = 0;
	int oldColor = (int)color;
	
	while ((choice < '1') || (choice > '8'))
	{
		color = 8; printf("(1)Black  ");
		color = 9; printf("(2)Blue   ");
		color = 10; printf("(3)Green  ");
		color = 11; printf("(4)Cyan\n");
		color = 12; printf("(5)Red    ");
		color = 13; printf("(6)Purple ");
		color = 14; printf("(7)Yellow ");
		color = 15; printf("(8)White\n");
		
		color = oldColor;
		
		printf("New color? ");
		choice = getc();
		printf("%c\n", choice);
	}
	
	color = choice - 41	;
}






















